/*
 * Copyright 2023-2025, Haiku, Inc. All rights reserved.
 * Distributed under the terms of the MIT License.
 *
 * 龙芯LoongArch64架构异常处理实现
 * 本文件实现LoongArch64架构的异常处理机制，包括各类异常的处理函数
 */

#include <KernelExport.h>

#include <arch/cpu.h>
#include <arch/int.h>
#include <debug.h>
#include <kernel.h>
#include <kscheduler.h>
#include <vm/vm.h>
#include <vm/vm_page.h>
#include <vm/vm_types.h>
#include <vm/VMAddressSpace.h>

#include "arch_int_private.h"

// 定义调试输出宏
#define TRACE_EXCEPTIONS
#ifdef TRACE_EXCEPTIONS
#define TRACE(x...) dprintf("arch_exceptions: " x)
#else
#define TRACE(x...) ;
#endif

// 定义LoongArch64异常类型
#define EXC_INT 0x0  // 中断
#define EXC_PIL 0x1  // 页无效例外（加载）
#define EXC_PIS 0x2  // 页无效例外（存储）
#define EXC_PIF 0x3  // 页无效例外（取指）
#define EXC_PME 0x4  // 页修改例外
#define EXC_PNR 0x5  // 页不可读例外
#define EXC_PNX 0x6  // 页不可执行例外
#define EXC_PPI 0x7  // 页特权等级不合规例外
#define EXC_ADEF 0x8 // 地址错例外（取指）
#define EXC_ADEM 0x9 // 地址错例外（内存）
#define EXC_ALE 0xa  // 地址未对齐例外
#define EXC_BCE 0xb  // 边界检查例外
#define EXC_SYS 0xc  // 系统调用例外
#define EXC_BRK 0xd  // 断点例外
#define EXC_INE 0xe  // 指令不存在例外
#define EXC_IPE 0xf  // 指令特权等级错例外
#define EXC_FPD 0x10 // 浮点禁用例外
#define EXC_FPE 0x11 // 浮点例外
#define EXC_DBP 0x12 // 调试例外（数据断点）
#define EXC_IBP 0x13 // 调试例外（指令断点）

// 定义CSR寄存器地址
#define CSR_CRMD 0x0   // 当前模式信息
#define CSR_PRMD 0x1   // 上一次模式信息
#define CSR_EUEN 0x2   // 扩展单元使能
#define CSR_MISC 0x3   // 杂项
#define CSR_ECFG 0x4   // 异常配置
#define CSR_ESTAT 0x5  // 异常状态
#define CSR_ERA 0x6    // 异常返回地址
#define CSR_BADV 0x7   // 异常访问地址
#define CSR_BADI 0x8   // 异常指令

// 异常处理函数声明
static void handle_page_fault(iframe* frame, uint32 excode, addr_t fault_addr);
static void handle_alignment_exception(iframe* frame, addr_t fault_addr);
static void handle_undefined_instruction(iframe* frame);
static void handle_fpu_exception(iframe* frame);
static void handle_debug_exception(iframe* frame, uint32 excode);

// 异常处理入口函数
extern "C" void
loongarch64_exception_entry(iframe* frame)
{
    // 获取异常状态寄存器
    uint32 estat;
    asm volatile("csrrd %0, %1" : "=r"(estat) : "i"(CSR_ESTAT));
    
    // 提取异常码
    uint32 excode = (estat >> 16) & 0x3f;
    
    // 获取异常访问地址
    addr_t fault_addr = frame->badv;
    
    // 根据异常类型分发处理
    switch (excode) {
        case EXC_INT:
            // 中断处理
            // 获取中断号
            uint32 irq = estat & 0xff;
            loongarch64_handle_interrupt(irq);
            break;
            
        case EXC_PIL:
        case EXC_PIS:
        case EXC_PIF:
        case EXC_PME:
        case EXC_PNR:
        case EXC_PNX:
        case EXC_PPI:
            // 页错误处理
            handle_page_fault(frame, excode, fault_addr);
            break;
            
        case EXC_ADEF:
        case EXC_ADEM:
        case EXC_ALE:
            // 地址错误和对齐错误处理
            handle_alignment_exception(frame, fault_addr);
            break;
            
        case EXC_SYS:
            // 系统调用处理
            // 系统调用由专门的入口点处理，不应该到达这里
            panic("System call exception should not reach here");
            break;
            
        case EXC_BRK:
            // 断点处理
            // 在用户模式下，发送SIGTRAP信号
            if ((frame->crmd & 0x3) == 0x3) { // 用户模式
                send_signal(thread_get_current_thread_id(), SIGTRAP);
            } else {
                // 内核模式断点，进入内核调试器
                kernel_debugger("Breakpoint");
            }
            break;
            
        case EXC_INE:
        case EXC_IPE:
            // 指令错误处理
            handle_undefined_instruction(frame);
            break;
            
        case EXC_FPD:
        case EXC_FPE:
            // 浮点异常处理
            handle_fpu_exception(frame);
            break;
            
        case EXC_DBP:
        case EXC_IBP:
            // 调试异常处理
            handle_debug_exception(frame, excode);
            break;
            
        default:
            // 未知异常处理
            panic("Unknown exception: excode=%u, fault_addr=%#" B_PRIxADDR, excode, fault_addr);
            break;
    }
}

// 页错误处理函数
static void
handle_page_fault(iframe* frame, uint32 excode, addr_t fault_addr)
{
    TRACE("Page fault: excode=%u, fault_addr=%#" B_PRIxADDR ", era=%#" B_PRIxADDR "\n",
          excode, fault_addr, frame->era);
    
    // 确定错误类型
    uint32 fault_type = 0;
    switch (excode) {
        case EXC_PIL:
        case EXC_PIF:
        case EXC_PNR:
            // 读错误
            fault_type = PAGE_FAULT_READ;
            break;
            
        case EXC_PIS:
        case EXC_PME:
            // 写错误
            fault_type = PAGE_FAULT_WRITE;
            break;
            
        case EXC_PNX:
            // 执行错误
            fault_type = PAGE_FAULT_EXECUTE;
            break;
            
        case EXC_PPI:
            // 权限错误
            if ((frame->crmd & 0x3) == 0x3) { // 用户模式
                fault_type = PAGE_FAULT_USER;
            }
            break;
    }
    
    // 检查是否在用户模式
    if ((frame->crmd & 0x3) == 0x3) { // 用户模式
        fault_type |= PAGE_FAULT_USER;
    }
    
    // 调用VM系统处理页错误
    vm_page_fault(fault_addr, frame->era, fault_type);
}

// 地址对齐错误处理函数
static void
handle_alignment_exception(iframe* frame, addr_t fault_addr)
{
    TRACE("Alignment exception: fault_addr=%#" B_PRIxADDR ", era=%#" B_PRIxADDR "\n",
          fault_addr, frame->era);
    
    // 检查是否在用户模式
    if ((frame->crmd & 0x3) == 0x3) { // 用户模式
        // 发送SIGBUS信号给用户进程
        send_signal(thread_get_current_thread_id(), SIGBUS);
    } else {
        // 内核模式对齐错误，这是严重错误
        panic("Kernel alignment exception: fault_addr=%#" B_PRIxADDR ", era=%#" B_PRIxADDR,
              fault_addr, frame->era);
    }
}

// 未定义指令处理函数
static void
handle_undefined_instruction(iframe* frame)
{
    TRACE("Undefined instruction: era=%#" B_PRIxADDR "\n", frame->era);
    
    // 获取异常指令
    uint32 instruction;
    asm volatile("csrrd %0, %1" : "=r"(instruction) : "i"(CSR_BADI));
    
    // 检查是否在用户模式
    if ((frame->crmd & 0x3) == 0x3) { // 用户模式
        // 发送SIGILL信号给用户进程
        send_signal(thread_get_current_thread_id(), SIGILL);
    } else {
        // 内核模式未定义指令，这是严重错误
        panic("Kernel undefined instruction: instruction=%#x, era=%#" B_PRIxADDR,
              instruction, frame->era);
    }
}

// 浮点异常处理函数
static void
handle_fpu_exception(iframe* frame)
{
    TRACE("FPU exception: era=%#" B_PRIxADDR "\n", frame->era);
    
    // 检查是否是浮点禁用异常
    uint32 excode = (frame->estat >> 16) & 0x3f;
    if (excode == EXC_FPD) {
        // 浮点禁用异常，需要启用FPU
        Thread* thread = thread_get_current_thread();
        
        // 启用FPU
        uint32 euen;
        asm volatile("csrrd %0, %1" : "=r"(euen) : "i"(CSR_EUEN));
        euen |= 0x1; // 设置FPU使能位
        asm volatile("csrwr %0, %1" : : "r"(euen), "i"(CSR_EUEN));
        
        // 标记线程FPU已启用
        thread->arch_info.fpuEnabled = true;
        
        // 如果需要，分配FPU状态
        if (thread->arch_info.fpuState == NULL) {
            thread->arch_info.fpuState = (fpu_context*)malloc(sizeof(fpu_context));
            if (thread->arch_info.fpuState == NULL) {
                panic("Failed to allocate FPU state");
            }
            // 初始化FPU状态
            memset(thread->arch_info.fpuState, 0, sizeof(fpu_context));
        }
    } else {
        // 浮点计算异常
        // 检查是否在用户模式
        if ((frame->crmd & 0x3) == 0x3) { // 用户模式
            // 发送SIGFPE信号给用户进程
            send_signal(thread_get_current_thread_id(), SIGFPE);
        } else {
            // 内核模式浮点异常，这是严重错误
            panic("Kernel FPU exception: era=%#" B_PRIxADDR, frame->era);
        }
    }
}

// 调试异常处理函数
static void
handle_debug_exception(iframe* frame, uint32 excode)
{
    TRACE("Debug exception: excode=%u, era=%#" B_PRIxADDR "\n", excode, frame->era);
    
    // 检查是否在用户模式
    if ((frame->crmd & 0x3) == 0x3) { // 用户模式
        // 发送SIGTRAP信号给用户进程
        send_signal(thread_get_current_thread_id(), SIGTRAP);
    } else {
        // 内核模式调试异常，进入内核调试器
        if (excode == EXC_DBP) {
            kernel_debugger("Data breakpoint");
        } else { // EXC_IBP
            kernel_debugger("Instruction breakpoint");
        }
    }
}

// 初始化异常向量表
void
init_exception_vectors(void)
{
    // 设置异常入口地址
    extern void loongarch64_exception_vector(void);
    asm volatile(
        "la.abs $t0, %0\n"  // 加载异常向量表地址
        "csrwr $t0, %1\n"   // 设置异常入口地址
        : 
        : "i"(loongarch64_exception_vector), "i"(CSR_EENTRY)
        : "t0"
    );
    
    // 配置异常处理
    // 启用所有异常
    uint32 ecfg = 0xffffffffU;
    asm volatile("csrwr %0, %1" : : "r"(ecfg), "i"(CSR_ECFG));
    
    TRACE("Exception vectors initialized\n");
}